﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CoreUtils.WIL
{
    /// <summary>
    /// Feature point matching (key point) functions from WIL library
    /// </summary>
    public class Matching
    {
        public static FVIL.Data.CFviImage GSearch(FVIL.Data.CFviImage src, FVIL.Data.CFviPattern pattern,
            int count, int score1, int score2)
        {
            FVIL.GSearch.CFviGSearch parser = new FVIL.GSearch.CFviGSearch();
            if (FVIL.ErrorCode._SUCCESS != FVIL.GSearch.CFviGSearch.CheckValidity(src))
                throw new Exception("Invalid image!");
            parser.Open(src.HorzSize, src.VertSize, 1024, -1);
            parser.SrcImages[0] = src;

            FVIL.GSearch.CFviGSearchParam param = parser.Param;
            param.Count = count;
            param.Precision = FVIL.GSearch.Precision.SuperHigh;
            param.Complexity = 1;
            param.Score1st = score1;
            param.Score2nd = score2;
            param.EdgeDetect = false;
            parser.OpenPattern(pattern, FVIL.GSearch.FilterMode.NormalPattern);
            parser.Execute();
            FVIL.Data.CFviImage dst = new FVIL.Data.CFviImage();
            {
                FVIL.GDI.CFviDisplay display = new FVIL.GDI.CFviDisplay();
                display.Image = src;
                display.DisplayRect = src.Window;
                FVIL.GDI.CFviOverlay pOverlay0 = new FVIL.GDI.CFviOverlay();
                pOverlay0.Scaling = true;
                display.Overlays.Add(pOverlay0);
                FVIL.Data.CFviPoint pat_mark = pattern.CenterMark;
                FVIL.Data.CFviRectangle pat_rect = pattern.RegistRect;

                for (int i = 0; i < parser.Result.Count; i++)
                {
                    FVIL.GSearch.CFviGSearchData data = parser.Result[i];
                    FVIL.Data.CFviPoint mark = data.Position;
                    FVIL.Data.CFviRectangle rect = new FVIL.Data.CFviRectangle();
                    rect.St = new FVIL.Data.CFviPoint(mark.X - pat_mark.X, mark.Y - pat_mark.Y);
                    rect.Ed = new FVIL.Data.CFviPoint(rect.X + pat_rect.Width, rect.Y + pat_rect.Height);
                    System.String score = data.Score.ToString();
                    FVIL.GDI.CFviGdiImage _fill = new FVIL.GDI.CFviGdiImage();
                    _fill.SetSize(1, 1, 32);
                    _fill.Position = rect.St;
                    _fill.Alpha = 0x3F;
                    _fill.StretchHorzSize = (int)(rect.Width);
                    _fill.StretchVertSize = (int)(rect.Height);
                    _fill.SetPixelRGB(0, 0, System.Drawing.Color.FromArgb(0xFF, 0x00, 0x00, 0xFF));
                    FVIL.GDI.CFviGdiRectangle _rect = new FVIL.GDI.CFviGdiRectangle(rect);
                    _rect.Pen.Color = System.Drawing.Color.FromArgb(0x00, 0x00, 0xFF);

                    FVIL.GDI.CFviGdiPoint _mark = new FVIL.GDI.CFviGdiPoint(mark);
                    _mark.Size = new System.Drawing.Size(5, 5);
                    _mark.Pen.Color = System.Drawing.Color.FromArgb(0xFF, 0x00, 0x00);

                    FVIL.GDI.CFviGdiString _score = new FVIL.GDI.CFviGdiString();
                    _score.Text = score.ToString();
                    _score.Position = mark;
                    _score.Align = FVIL.GDI.TextAlign.Right | FVIL.GDI.TextAlign.Bottom;
                    _score.BkMode = FVIL.GDI.BkMode.Transparent;
                    _score.Color = System.Drawing.Color.FromArgb(0xFF, 0xFF, 0x00);

                    pOverlay0.Figures.Add(_fill);
                    pOverlay0.Figures.Add(_rect);
                    pOverlay0.Figures.Add(_mark);
                    pOverlay0.Figures.Add(_score);
                }

                display.SaveImage(dst, display.DisplayRect, 1.0);
            }
            return dst;
        }

        public static FVIL.Data.CFviImage GSearch2(FVIL.Data.CFviImage src, FVIL.Data.CFviPattern pattern,
            int count)
        {
            FVIL.Data.CFviImage dst = new FVIL.Data.CFviImage();
            try
            {
                FVIL.GS2.CFviGS2 parser = new FVIL.GS2.CFviGS2();
                parser.Open();
                FVIL.GS2.CFviGS2Pattern gs2pattern = new FVIL.GS2.CFviGS2Pattern();
                gs2pattern.Create(pattern, FVIL.GS2.Filter.Smooth);

                if (FVIL.ErrorCode._SUCCESS != FVIL.GS2.CFviGS2.CheckValidity(src))
                    throw new Exception("Invalid Image!");

                parser.SrcImages[0] = src;
                parser.ExecuteOption = FVIL.GS2.ExecuteOption.Default;
                parser.ContinueOption = FVIL.GS2.ContinueOption.None;
                parser.Param.Count = count;
                parser.Param.EdgeDetect = true;
                parser.Param.ReverseMode = false;
                parser.Param.Precision = FVIL.GS2.Precision.Normal;
                parser.Param.Complexity = 1;
                parser.Result = new FVIL.GS2.CFviGS2Result();
                parser.Pattern = gs2pattern;
                parser.Execute();
                {
                    FVIL.GDI.CFviDisplay display = new FVIL.GDI.CFviDisplay();
                    display.Image = src;
                    display.DisplayRect = src.Window;
                    FVIL.GDI.CFviOverlay pOverlay0 = new FVIL.GDI.CFviOverlay();
                    pOverlay0.Scaling = true;
                    display.Overlays.Add(pOverlay0);
                    FVIL.Data.CFviPoint pat_mark = pattern.CenterMark;
                    FVIL.Data.CFviRectangle pat_rect = pattern.RegistRect;
                    fvalgcli.F_GS_RESULT_PTR data_ptr = parser.Result.Address;

                    for (int i = 0; i < parser.Result.Count; i++)
                    {
                        FVIL.Data.CFviPoint mark =
                            new FVIL.Data.CFviPoint(data_ptr[0].x / 100, data_ptr[0].y / 100);
                        FVIL.Data.CFviRectangle rect = new FVIL.Data.CFviRectangle();
                        rect.St = new FVIL.Data.CFviPoint(mark.X - pat_mark.X, mark.Y - pat_mark.Y);
                        rect.Ed = new FVIL.Data.CFviPoint(rect.X + pat_rect.Width, rect.Y + pat_rect.Height);
                        System.String score = data_ptr[0].score.ToString();

                        FVIL.GDI.CFviGdiImage _fill = new FVIL.GDI.CFviGdiImage();
                        _fill.SetSize(1, 1, 32);
                        _fill.Position = rect.St;
                        _fill.Alpha = 0x3F;
                        _fill.StretchHorzSize = (int)(rect.Width);
                        _fill.StretchVertSize = (int)(rect.Height);
                        _fill.SetPixelRGB(0, 0, System.Drawing.Color.FromArgb(0xFF, 0x00, 0x00, 0xFF));

                        FVIL.GDI.CFviGdiRectangle _rect = new FVIL.GDI.CFviGdiRectangle(rect);
                        _rect.Pen.Color = System.Drawing.Color.FromArgb(0x00, 0x00, 0xFF);

                        FVIL.GDI.CFviGdiPoint _mark = new FVIL.GDI.CFviGdiPoint(mark);
                        _mark.Size = new System.Drawing.Size(5, 5);
                        _mark.Pen.Color = System.Drawing.Color.FromArgb(0xFF, 0x00, 0x00);

                        FVIL.GDI.CFviGdiString _score = new FVIL.GDI.CFviGdiString();
                        _score.Text = score.ToString();
                        _score.Position = mark;
                        _score.Align = FVIL.GDI.TextAlign.Right | FVIL.GDI.TextAlign.Bottom;
                        _score.BkMode = FVIL.GDI.BkMode.Transparent;
                        _score.Color = System.Drawing.Color.FromArgb(0xFF, 0xFF, 0x00);

                        pOverlay0.Figures.Add(_fill);
                        pOverlay0.Figures.Add(_rect);
                        pOverlay0.Figures.Add(_mark);
                        pOverlay0.Figures.Add(_score);

                        data_ptr += 1;
                    }
                    display.SaveImage(dst, display.DisplayRect, 1.0);
                }
            }
            catch (FVIL.CFviException ex)
            {
                CoreUtils.Common.ErrorHandler.handlerError(CoreUtils.Common.ErrorHandler.ErrorType.ERROR,
                            ex.Message);
            }
            return dst;
        }

        public static FVIL.Data.CFviImage FPM(FVIL.Data.CFviImage src, FVIL.Data.CFviPattern pattern,
            int count, int edgeThreshold)
        {
            FVIL.FPM.CFviFPMCorrelationEdge parser = new FVIL.FPM.CFviFPMCorrelationEdge();
            if (FVIL.ErrorCode._SUCCESS != FVIL.FPM.CFviFPMCorrelationEdge.CheckValidity(src))
                throw new Exception("Invalid Image!");
            parser.SrcImages[0] = src;
            parser.ExecuteFlag = FVIL.FPM.ExecuteFlag.On;
            parser.ExecuteAreaScoreFlag = FVIL.FPM.ExecuteAreaScoreFlag.Off;
            parser.MatchMode = FVIL.FPM.MatchMode.HighSpeed;
            parser.CenterMarkAutoFlag = FVIL.FPM.CenterMarkAutoFlag.Manual;

            FVIL.FPM.CFviFPMParam param = parser.Param;
            param.Count = 10;
            param.AngleMin = new FVIL.Data.CFviAngle(-180);
            param.AngleMax = new FVIL.Data.CFviAngle(180);
            param.ScaleMin = 100;
            param.ScaleMax = 100;
            param.CompressionLevel = 3;
            param.ErrorRange = 1;
            param.ScoreThresholdL = 50;
            param.ScoreThresholdH = 50;

            FVIL.FPM.CFviFPMParamRefine paramR = parser.ParamRefine;
            paramR.ErrorRange = 1;
            paramR.ScoreThreshold = 60;

            FVIL.FPM.CFviFPMParamAreaScore paramA = parser.ParamAreaScore;
            paramA.ErrorRangeTx = 0.5;
            paramA.ErrorRangeTy = 0.5;
            paramA.ErrorRangeTq = 0.5;
            paramA.ErrorRangeTs = 0.5;
            paramA.ErrorRange = 1;
            paramA.NoiseWeight = 0.2;
            paramA.ScoreThreshold = 60;

            FVIL.FPM.CFviFPMParamCorrelationEdge paramE = parser.ParamEdge;
            paramE.RegionWidth = 13;
            paramE.RegionHeight = 5;
            paramE.SigmoidK = 1.0;
            paramE.VarThreshold = 25.0;
            paramE.EdgeThreshold = 160;
            paramE.NmsLength = 6;
            parser.OpenPattern(pattern);
            parser.Execute();
            return SaveFPMResult(src, pattern, parser.Result);
        }

        public static FVIL.Data.CFviImage SaveFPMResult(FVIL.Data.CFviImage src, FVIL.Data.CFviPattern pattern,
            FVIL.FPM.CFviFPMResult result)
        {
            FVIL.GDI.CFviDisplay display = new FVIL.GDI.CFviDisplay();
            display.Image = src;
            display.DisplayRect = src.Window;

            FVIL.GDI.CFviOverlay pOverlay0 = new FVIL.GDI.CFviOverlay();
            pOverlay0.Scaling = true;
            display.Overlays.Add(pOverlay0);

            FVIL.Data.CFviPoint pat_mark = pattern.CenterMark;
            FVIL.Data.CFviRectangle pat_rect = pattern.RegistRect;

            double width = pat_rect.Width;
            double height = pat_rect.Height;

            for (int i = 0; i < result.Count; i++)
            {
                FVIL.FPM.CFviFPMData data = result[i];

                FVIL.Data.CFviPoint pos = data.Position;
                FVIL.Data.CFviPoint mark = data.CenterMark;
                FVIL.Data.CFviPoint trans = data.TransferAmount;
                FVIL.Data.CFviRectangle rect = new FVIL.Data.CFviRectangle();
                double scale = data.Scale;
                int score = data.Score;

                {
                    FVIL.Data.CFviPoint center = new FVIL.Data.CFviPoint();
                    center.X = mark.X + trans.X;
                    center.Y = mark.Y + trans.Y;

                    FVIL.Data.CFviPoint st = new FVIL.Data.CFviPoint();
                    FVIL.Data.CFviPoint ed = new FVIL.Data.CFviPoint();
                    st.X = center.X - (scale / 100.0 * mark.X);
                    st.Y = center.Y - (scale / 100.0 * mark.Y);
                    ed.X = st.X + (scale / 100.0 * width);
                    ed.Y = st.Y + (scale / 100.0 * height);
                    center.X -= st.X;
                    center.Y -= st.Y;

                    rect.St = st;
                    rect.Ed = ed;
                    rect.Center = center;
                    rect.Angle = data.Angle;
                }

                FVIL.GDI.CFviGdiImage _fill = new FVIL.GDI.CFviGdiImage();
                _fill.SetSize(1, 1, 32);
                _fill.SetPixelRGB(0, 0, System.Drawing.Color.FromArgb(0xFF, 0x00, 0x00, 0xFF));    // A,R,G,B
                _fill.Position = rect.St;
                _fill.Axis = rect.Center;
                _fill.Angle = rect.Angle;
                _fill.Alpha = 0x3F;
                _fill.AlphaFormat = 0;
                _fill.StretchHorzSize = (int)width;
                _fill.StretchVertSize = (int)height;

                FVIL.GDI.CFviGdiRectangle _rect = new FVIL.GDI.CFviGdiRectangle(rect);
                _rect.Pen.Color = System.Drawing.Color.FromArgb(0x00, 0x00, 0xFF);

                FVIL.GDI.CFviGdiPoint _pos = new FVIL.GDI.CFviGdiPoint(pos);
                _pos.Size = new System.Drawing.Size(5, 5);
                _pos.Pen.Color = System.Drawing.Color.FromArgb(0xFF, 0x00, 0x00);
                _pos.Angle = rect.Angle;

                FVIL.GDI.CFviGdiString _score = new FVIL.GDI.CFviGdiString();
                _score.Text = score.ToString();
                _score.Position = pos;
                _score.Align = FVIL.GDI.TextAlign.Right | FVIL.GDI.TextAlign.Bottom;
                _score.BkMode = FVIL.GDI.BkMode.Transparent;
                _score.Color = System.Drawing.Color.FromArgb(0xFF, 0xFF, 0x00);
                _score.Angle = rect.Angle;

                FVIL.GDI.CFviGdiString _scale = new FVIL.GDI.CFviGdiString();
                _scale.Text = scale.ToString("0");
                _scale.Position = pos;
                _scale.Align = FVIL.GDI.TextAlign.Right | FVIL.GDI.TextAlign.Top;
                _scale.BkMode = FVIL.GDI.BkMode.Transparent;
                _scale.Color = System.Drawing.Color.FromArgb(0x7F, 0xFF, 0x00);
                _scale.Angle = rect.Angle;

                FVIL.GDI.CFviGdiString _angle = new FVIL.GDI.CFviGdiString();
                _angle.Text = rect.Angle.Degree.ToString("0");
                _angle.Position = new FVIL.Data.CFviPoint(pos.X, pos.Y + _scale.Font.Height);
                _angle.Align = FVIL.GDI.TextAlign.Right | FVIL.GDI.TextAlign.Top;
                _angle.BkMode = FVIL.GDI.BkMode.Transparent;
                _angle.Color = System.Drawing.Color.FromArgb(0x00, 0xFF, 0xFF);
                _angle.Angle = rect.Angle;
                _angle.Axis = new FVIL.Data.CFviPoint(0, -_scale.Font.Height);

                pOverlay0.Figures.Add(_fill);
                pOverlay0.Figures.Add(_rect);
                pOverlay0.Figures.Add(_pos);
                pOverlay0.Figures.Add(_score);
                pOverlay0.Figures.Add(_scale);
                pOverlay0.Figures.Add(_angle);
            }

            FVIL.Data.CFviImage dst = new FVIL.Data.CFviImage();
            display.SaveImage(dst, display.DisplayRect, 1.0);
            return dst;
        }

        public static FVIL.Data.CFviImage FPM2(FVIL.Data.CFviImage src, FVIL.Data.CFviPattern pattern,
            int count, int edgeThreshold)
        {
            FVIL.FPM2.CFviFPM2Matching matching = new FVIL.FPM2.CFviFPM2Matching();
            FVIL.FPM2.CFviFPM2Param param = matching.Param;
            param.Count = count;
            param.AngleMin = new FVIL.Data.CFviAngle(-180);
            param.AngleMax = new FVIL.Data.CFviAngle(180);
            param.ScaleMin = 100;
            param.ScaleMax = 100;
            param.CompressionLevel = 3;
            param.ErrorRange = 1;
            param.ScoreThresholdL = 50;
            param.ScoreThresholdH = 50;

            FVIL.FPM2.CFviFPM2ParamAreaScore paramA = matching.ParamAreaScore;
            paramA.ErrorRangeTx = 0.5;
            paramA.ErrorRangeTy = 0.5;
            paramA.ErrorRangeTq = 0.5;
            paramA.ErrorRangeTs = 0.5;
            paramA.ErrorRange = 1;
            paramA.NoiseWeight = 0.2;
            paramA.ScoreThreshold = 60;

            FVIL.FPM2.CFviFPM2ParamEssential paramE = matching.ParamEssential;
            paramE.ErrorRange = 1;
            paramE.ScoreThreshold = 60;

            FVIL.Edge.CFviEdge2DCorrelationParam edge_param = new FVIL.Edge.CFviEdge2DCorrelationParam();
            edge_param.RegionWidth = 13;
            edge_param.RegionHeight = 5;
            edge_param.SigmoidK = 1.0;
            edge_param.VarThreshold = 25.0;
            edge_param.EdgeThreshold = edgeThreshold;
            edge_param.NmsLength = 6;

            FVIL.FPM2.CFviFPM2Template templ =
                new FVIL.FPM2.CFviFPM2Template(pattern, FVIL.FPM2.MatchMode.Normal, edge_param);

            FVIL.FPM2.CFviFPM2Feature feature =
                new FVIL.FPM2.CFviFPM2Feature(src, FVIL.FPM2.MatchMode.Normal, edge_param, false);

            matching.Param = param;
            matching.ParamAreaScore = paramA;
            matching.ParamEssential = paramE;
            matching.Target = feature;
            matching.Template = templ;
            matching.EnableAreaScore = false;
            matching.EnableEssential = false;
            matching.Execute();
            return SaveFPMResult2(src, templ, matching);
        }

        public static FVIL.Data.CFviImage SaveFPMResult2(FVIL.Data.CFviImage src,
            FVIL.FPM2.CFviFPM2Template templ, FVIL.FPM2.CFviFPM2Matching matching)
        {
            FVIL.GDI.CFviDisplay display = new FVIL.GDI.CFviDisplay();
            display.Image = src;
            display.DisplayRect = src.Window;

            FVIL.GDI.CFviOverlay pOverlay0 = new FVIL.GDI.CFviOverlay();
            pOverlay0.Scaling = true;
            display.Overlays.Add(pOverlay0);

            FVIL.Data.CFviPoint offset = templ.Offset;
            double width = templ.Width;
            double height = templ.Height;

            for (int i = 0; i < matching.ResultSize; i++)
            {
                FVIL.FPM2.CFviFPM2Data data = matching[i];
                FVIL.Data.CFviPoint pos = data.Position;
                FVIL.Data.CFviAngle angle = data.Angle;
                double scale = data.Scale;
                int score = data.Score;

                FVIL.Data.CFviRectangle rect = new FVIL.Data.CFviRectangle();
                rect.St = new FVIL.Data.CFviPoint(pos.X - offset.X, pos.Y - offset.Y);
                rect.Ed = new FVIL.Data.CFviPoint(rect.St.X + width, rect.St.Y + height);
                rect.Center = offset;
                rect.Angle = angle;

                FVIL.GDI.CFviGdiRectangle _rect = new FVIL.GDI.CFviGdiRectangle(rect);
                _rect.Pen.Color = System.Drawing.Color.FromArgb(0x00, 0x00, 0xFF);
                _rect.FillEnable = true;
                _rect.FillAlpha = 0x3F;
                _rect.FillColor = System.Drawing.Color.FromArgb(0xFF, 0x00, 0x00);

                FVIL.GDI.CFviGdiPoint _pos = new FVIL.GDI.CFviGdiPoint(pos);
                _pos.Size = new System.Drawing.Size(5, 5);
                _pos.Pen.Color = System.Drawing.Color.FromArgb(0xFF, 0x00, 0x00);
                _pos.Angle = rect.Angle;

                FVIL.GDI.CFviGdiString _score = new FVIL.GDI.CFviGdiString();
                _score.Text = score.ToString();
                _score.Position = pos;
                _score.Align = FVIL.GDI.TextAlign.Right | FVIL.GDI.TextAlign.Bottom;
                _score.BkMode = FVIL.GDI.BkMode.Transparent;
                _score.Color = System.Drawing.Color.FromArgb(0xFF, 0xFF, 0x00);
                _score.Angle = rect.Angle;

                FVIL.GDI.CFviGdiString _scale = new FVIL.GDI.CFviGdiString();
                _scale.Text = scale.ToString("0");
                _scale.Position = pos;
                _scale.Align = FVIL.GDI.TextAlign.Right | FVIL.GDI.TextAlign.Top;
                _scale.BkMode = FVIL.GDI.BkMode.Transparent;
                _scale.Color = System.Drawing.Color.FromArgb(0x7F, 0xFF, 0x00);
                _scale.Angle = rect.Angle;

                FVIL.GDI.CFviGdiString _angle = new FVIL.GDI.CFviGdiString();
                _angle.Text = rect.Angle.Degree.ToString("0");
                _angle.Position = new FVIL.Data.CFviPoint(pos.X, pos.Y + _scale.Font.Height);
                _angle.Align = FVIL.GDI.TextAlign.Right | FVIL.GDI.TextAlign.Top;
                _angle.BkMode = FVIL.GDI.BkMode.Transparent;
                _angle.Color = System.Drawing.Color.FromArgb(0x00, 0xFF, 0xFF);
                _angle.Angle = rect.Angle;
                _angle.Axis = new FVIL.Data.CFviPoint(0, -_scale.Font.Height);

                pOverlay0.Figures.Add(_rect);
                pOverlay0.Figures.Add(_pos);
                pOverlay0.Figures.Add(_score);
                pOverlay0.Figures.Add(_scale);
                pOverlay0.Figures.Add(_angle);
            }

            FVIL.Data.CFviImage dst = new FVIL.Data.CFviImage();
            display.SaveImage(dst, display.DisplayRect, 1.0);
            return dst;
        }

    }
}
